package com.songyang.yougnsql.core.table;

import com.songyang.yougnsql.common.ErrorMsg;
import com.songyang.yougnsql.common.ExceptionHandler;
import com.songyang.yougnsql.common.LogHelper;
import com.songyang.yougnsql.common.YoungSQLException;
import com.songyang.yougnsql.core.index.BPlusTree;
import com.songyang.yougnsql.core.parser.statement.SingleExpression;
import com.songyang.yougnsql.core.table.res.FieldCalRes;
import com.songyang.yougnsql.core.table.res.ParseValueRes;
import com.songyang.yougnsql.core.transaction.TransactionManager;
import com.songyang.yougnsql.core.utils.ByteUtils;
import com.songyang.yougnsql.core.utils.ParseStringRes;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Arrays;
import java.util.List;

/**
 * @ClassName Field
 * @Description 表示字段的信息
 * [FieldName][TypeName][IndexUid]
 * 如果field无索引，IndexUid为0
 * @date 2022/12/29 10:16
 * @Author yanceysong
 * @Version 1.0
 */
public class Field {
    private final Logger logger = LogManager.getLogger(Field.class);
    public static final int FIELD_IS_NOT_INDEX = 0;
    long uid;
    String fieldName;
    String fieldType;
    private final Table table;
    private long index;
    private BPlusTree bPlusTree;

    public Field(long uid, Table tb) {
        this.uid = uid;
        this.table = tb;
    }

    public Field(Table tb, String fieldName, String fieldType, long index) {
        this.table = tb;
        this.fieldName = fieldName;
        this.fieldType = fieldType;
        this.index = index;
    }

    /**
     * 通过一个表和uid加载一个字段
     *
     * @param tb  表
     * @param uid 字段的uid
     * @return 字段
     */
    public static Field loadField(Table tb, long uid) {
        byte[] raw = null;
        try {
            raw = ((TableManagerImpl) tb.tbm).versionManager.read(TransactionManager.SUPER_XID, uid);
        } catch (Exception e) {
            ExceptionHandler.handler(e);
        }
        assert raw != null;
        return new Field(uid, tb).parseSelf(raw);
    }

    /**
     * 创建一个字段
     *
     * @param tb        表
     * @param xid       事务的id
     * @param fieldName 字段的名字
     * @param fieldType 字段的类型
     * @param indexed   是否是索引
     * @return 创建好的字段
     * @throws Exception 异常
     */
    public static Field createField(Table tb, long xid, String fieldName, String fieldType, boolean indexed) throws Exception {
        //检查字段的类型
        typeCheck(fieldType);
        //创建一个字段
        Field f = new Field(tb, fieldName, fieldType, 0);
        if (indexed) {
            //索引的根节点
            long index = BPlusTree.create(((TableManagerImpl) tb.tbm).dataManager);
            BPlusTree bt = BPlusTree.loadBPlusTree(index, ((TableManagerImpl) tb.tbm).dataManager);
            f.index = index;
            f.bPlusTree = bt;

        }
        //将字段的信息插入的文件并且赋值给字段内部的uid
        f.persistSelf(xid);
        return f;
    }

    /**
     * 检查字段的类型是否符合规范
     *
     * @param fieldType 字段的类型
     * @throws Exception 异常
     */
    private static void typeCheck(String fieldType) throws Exception {
        if (!"int".equals(fieldType) && !"bigint".equals(fieldType) && !"string".equals(fieldType)) {
            throw new YoungSQLException(ErrorMsg.INVALID_FIELD_EXCEPTION);
        }
    }

    /**
     * 将一个字段的数据进行拼接插入到文件当中
     *
     * @param xid 事务的id
     * @throws Exception 异常
     */
    private void persistSelf(long xid) throws Exception {
        byte[] nameRaw = ByteUtils.string2Byte(fieldName);
        byte[] typeRaw = ByteUtils.string2Byte(fieldType);
        byte[] indexRaw = ByteUtils.long2Byte(index);
        this.uid = ((TableManagerImpl) table.tbm).versionManager.insert(xid, ByteUtils.concat(nameRaw, typeRaw, indexRaw));
    }

    /**
     * 判断当前字段是否是索引
     *
     * @return 结果
     */
    public boolean isIndexed() {
        return index != FIELD_IS_NOT_INDEX;
    }

    /**
     * 类型再转成string返回
     *
     * @param v 待转化的值
     * @return 结果
     */
    public String printValue(Object v) {
        String str = null;
        switch (fieldType) {
            case "int":
                str = String.valueOf((int) v);
                break;
            case "bigint":
                str = String.valueOf((long) v);
                break;
            case "string":
                str = (String) v;
                break;
        }
        return str;
    }

    /**
     * 如果该字段是索引的话，那么将该条信息插入到b+树
     *
     * @param key 待插入的key
     * @param uid 资源的uid
     * @throws Exception 异常
     */
    public void insert(Object key, long uid) throws Exception {
        //b+树的key
        long uKey = value2Uid(key);
        LogHelper.info(logger,"insert:字段要插入b+树的key{}",uKey);
        bPlusTree.insert(uKey, uid);
    }

    /**
     * 根据数据解析自己
     *
     * @param raw 数据
     * @return 一个字段对象
     */
    private Field parseSelf(byte[] raw) {
        int position = 0;
        ParseStringRes res = ByteUtils.parseString(raw);
        fieldName = res.getStr();
        position += res.getNext();
        res = ByteUtils.parseString(Arrays.copyOfRange(raw, position, raw.length));
        fieldType = res.getStr();
        position += res.getNext();
        this.index = ByteUtils.parseLong(Arrays.copyOfRange(raw, position, position + 8));
        if (index != 0) {
            try {
                bPlusTree = BPlusTree.loadBPlusTree(index, ((TableManagerImpl) table.tbm).dataManager);
            } catch (Exception e) {
                ExceptionHandler.handler(e);
            }
        }
        return this;
    }

    /**
     * 将值转成字节后返回
     *
     * @param v 待转换的值
     * @return 转后的字节
     */
    public byte[] value2Raw(Object v) {
        byte[] raw = null;
        switch (fieldType) {
            case "int":
                raw = ByteUtils.int2Byte((int) v);
                break;
            case "bigint":
                raw = ByteUtils.long2Byte((long) v);
                break;
            case "string":
                raw = ByteUtils.string2Byte((String) v);
                break;
        }
        return raw;
    }

    /**
     * 执行表达式
     *
     * @param exp 表达式
     * @return 得到表达式需要查询的key范围
     * @throws Exception 异常
     */
    public FieldCalRes calExp(SingleExpression exp) throws Exception {
        Object v = null;
        FieldCalRes res = new FieldCalRes();
        switch (exp.compareOp) {
            case "<":
                res.left = 0;
                v = string2Value(exp.value);
                res.right = value2Uid(v);
                if (res.right > 0) {
                    //不算等于情况
                    res.right--;
                }
                break;
            case "=":
                v = string2Value(exp.value);
                res.left = value2Uid(v);
                res.right = res.left;
                break;
            case ">":
                res.right = Long.MAX_VALUE;
                v = string2Value(exp.value);
                res.left = value2Uid(v) + 1;
                break;
        }
        return res;
    }

    /**
     * 根据左右子树查询
     *
     * @param left  左
     * @param right 右
     * @return 查询结果的uid列表
     * @throws Exception 异常
     */
    public List<Long> search(long left, long right) throws Exception {
        return bPlusTree.searchRange(left, right);
    }

    /**
     * 字符串转成对应的类型
     *
     * @param str 待转化的字符串
     * @return 转换结果
     */
    public Object string2Value(String str) {
        switch (fieldType) {
            case "int":
                return Integer.parseInt(str);
            case "bigint":
                return Long.parseLong(str);
            case "string":
                return str;
        }
        return null;
    }

    /**
     * 类型转成uid，供构建索引使用
     *
     * @param key key
     * @return uid
     */
    public long value2Uid(Object key) {
        long uid = 0;
        switch (fieldType) {
            case "string":
                uid = ByteUtils.str2Uid((String) key);
                break;
            case "int":
                return (int) key;
            case "bigint":
                return (long) key;
        }
        return uid;
    }

    /**
     * 根据读取的字段结果判断是什么类型并进行解析
     *
     * @param raw 读取的数据
     * @return 解析的结果
     */
    public ParseValueRes parserValue(byte[] raw) {
        ParseValueRes res = new ParseValueRes();
        switch (fieldType) {
            case "int":
                res.v = ByteUtils.parseInt(Arrays.copyOf(raw, 4));
                res.shift = 4;
                break;
            case "bigint":
                res.v = ByteUtils.parseLong(Arrays.copyOf(raw, 8));
                res.shift = 8;
                break;
            case "string":
                ParseStringRes r = ByteUtils.parseString(raw);
                res.v = r.getStr();
                res.shift = r.getNext();
                break;
        }
        return res;
    }

    @Override
    public String toString() {
        return "(" +
                fieldName +
                ", " +
                fieldType +
                (index != FIELD_IS_NOT_INDEX ? ", Index" : ", NoIndex") +
                ")";
    }
}

